package auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.JdbcClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;

/**
 * @author zbs
 * @date 2020/10/26
 */
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private TokenStore redisTokenStore;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserDetailsService authUserDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;

    //1，配置客户端
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //将客户端配置在数据源中，并指定数据源和编码
        JdbcClientDetailsServiceBuilder jcds = clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
        //下面是将客户端配置在内存中
//        clients.inMemory()
//                .withClient("order-client")
//                .secret(passwordEncoder.encode("order-secret-8888"))
//                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
//                .accessTokenValiditySeconds(3600)
//                .scopes("all")
//                .and()
//                .withClient("user-client")
//                .secret(passwordEncoder.encode("user-secret-8888"))
//                //authorizedGrantTypes：
//                //1，authorization_code：授权码类型。
//                //2，implicit：隐式授权类型。
//                //3，password：资源所有者（即用户）密码类型。
//                //4，client_credentials：客户端凭据（客户端ID以及Key）类型。
//                //5，refresh_token：通过以上授权获得的刷新令牌来获取新的令牌
//                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
//                .accessTokenValiditySeconds(3600)  //token 的有效期
//                .scopes("all");//用来限制客户端访问的权限，在换取的 token 的时候会带上 scope 参数，只有在 scopes 定义内的，才可以正常换取 token
    }
    //2，设置客户端访问认证接口的权限
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();//允许客户端访问 OAuth2 授权接口，否则请求 token 会返回 401
        security.checkTokenAccess("isAuthenticated()");//允许已授权用户访问 checkToken 接口
        security.tokenKeyAccess("isAuthenticated()");//允许访问获取 token  接口
    }
    //3，配置认证参数
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)//支持 password 模式
                .userDetailsService(authUserDetailsService)
                .tokenStore(redisTokenStore);

    }
    @Bean
    public TokenStore redisTokenStore(RedisConnectionFactory factory){
        RedisTokenStore redisTokenStore = new RedisTokenStore(factory);
        redisTokenStore.setPrefix("oauth2:");//加个前缀免得redis中存储的太混乱
        //redisTokenStore.setSerializationStrategy(new GenericJacksonRedisTokenStoreSerializer());
        return redisTokenStore;
    }
//    @Bean
//    public DefaultTokenServices defaultTokenServices(){
//        //参考AuthorizationServerEndpointsConfigurer.createDefaultTokenServices()
//        DefaultTokenServices tokenServices = new DefaultTokenServices();
//        tokenServices.setTokenStore(tokenStore());
//        tokenServices.setSupportRefreshToken(true);
//        tokenServices.setReuseRefreshToken(reuseRefreshToken);
//        tokenServices.setClientDetailsService(clientDetailsService());
//        tokenServices.setTokenEnhancer(tokenEnhancer());
//        addUserDetailsService(tokenServices, this.userDetailsService);
//        return tokenServices;
//    }

    public static void main(String[] args) {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        //$2a$10$ZdDFYu13zfOjxCzaD3DWHuezVmR0Yg/yOvYT0dhY2IDsmip0TdEZ2
        System.out.println(bCryptPasswordEncoder.encode("123"));
    }
}
